/**
 * Copyright (C) 2011 Andrew C. Love (DNC) <dnc.app.sup@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dnc.cloak.framework.util.stream;

import java.io.IOException;
import java.io.InputStream;

public class StreamUtil {
	
    private static final int BUFFER_LENGTH = 4 * 1024;
        
    static public byte[] read(InputStream is) throws IOException {
        return read(is, BUFFER_LENGTH);
    }
    
    static public byte[] read(InputStream is, int bufferLength) throws IOException {
    	
        if (bufferLength < BUFFER_LENGTH) {
        	bufferLength = BUFFER_LENGTH;
        }
        
        byte[] buffer = null;
        byte[] tempBuffer = new byte[bufferLength];
        
        int bytes = bufferLength;
        int offset = 0;
        
        while (bytes == tempBuffer.length) {
            bytes = read(is, tempBuffer, 0, tempBuffer.length);
            
            if (bytes > 0) {
                // special case to optimize for a single read where chunk size == is.available() and everything is read 
                // do not allocate a copy buffer
                boolean peakAhead = false;
                int peakByte = 0;
                if (bytes == tempBuffer.length) {
                    peakByte = is.read();                           // peek ahead
                    if (peakByte == -1) {
                        return tempBuffer;
                    }
                    else {
                        peakAhead = true;
                    }
                }
                if (buffer == null) {                               // allocate initial buffer
                    buffer = new byte[bytes + (peakAhead ? 1 : 0)];
                    offset = 0;
                }
                else {                                              // reallocate buffer (and trim if read is complete)
                    byte[] temp = new byte[buffer.length + bytes + (peakAhead ? 1 : 0)];
                    System.arraycopy(buffer, 0, temp, 0, buffer.length);
                    offset = buffer.length;
                    buffer = temp;
                }
                System.arraycopy(tempBuffer, 0, buffer, offset, bytes);  // copy buffer
                if (peakAhead) {                                     // add peek-ahead byte
                    buffer[offset + bytes] = (byte)peakByte;
                }
            }
        }
        
        return buffer;
        
    }
        
    static public int read(InputStream is, byte buffer[], int offset, int length) throws IOException {
    	
        int total = 0;
        for (int read = 0; total < length; total += read) {
            read = is.read(buffer, offset + total, length - total);
            if (read == -1) {
                break;
            }
        }
        
        return total;
        
    }
    
}
